#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <fcntl.h>

#include <aura.h>
#include <auraplus.h>
#include <linknode.h>
#include <aura_wchar.h>

#include "thread.h"

EXTERN_C Aura_ECode MaskSignal(sigset_t *pSigNewMask, sigset_t *pSigOldMask);
EXTERN_C void UnmaskSignal(sigset_t *pSigOldMask);
EXTERN_C Aura_ECode SetMilliTimer(Aura_Millisecond time);
EXTERN_C Aura_ECode SetTimeOut(Aura_Millisecond timeOut, Aura_WaitResult * pResult);

EXTERN Aura_ECode CreateAnonymDir(/*out*/char *pDirName);

struct MutexInfo {
    Aura_UInt32       m_uMagicnumberStart;
    char         m_szName[AURA_MAX_DIR];
    Aura_Bool      m_bCreate;
    int          m_iSemId;
    Aura_UInt32       m_uMagicnumberEnd;
    MutexInfo()
        {
            m_uMagicnumberStart = MAGIC_START_NUMBER;
            m_uMagicnumberEnd = MAGIC_END_NUMBER;
            m_bCreate = AURA_TRUE;
        }
};

Aura_ECode OpenMutex(Aura_PWChar wszName, Aura_Handle *phMutex)
{
    key_t semKey;
    MutexInfo *pMutexInfo;

    if(AURA_NULL == wszName){
        return AURA_E_NOT_IMPLEMENTED;
    }

    assert(phMutex);

    pMutexInfo = new MutexInfo();
    if(AURA_NULL == pMutexInfo){
        PERROR("Error:Create Mutex failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
        return AURA_E_OUT_OF_MEMORY;
    }

    UnicToAnsi(pMutexInfo->m_szName, wszName);

    pMutexInfo->m_bCreate = AURA_FALSE;

    semKey = ftok(pMutexInfo->m_szName, 'a');

    pMutexInfo->m_iSemId = semget( semKey, 1, 0666 );
    if (-1  == pMutexInfo->m_iSemId){
        PERROR("Error:Open Mutex failed! errno:%d.\
        file:%s. line:%d. \n",
        errno, __FILE__, __LINE__);
        delete pMutexInfo;
        return AURA_E_INVALID_OPERATION;
    }

    *phMutex = (Aura_Handle)pMutexInfo;
    return AURA_NOERROR;
}

Aura_ECode CreateMutex(Aura_PWChar wszName, Aura_Handle *phMutex)
{
    key_t semKey;
    MutexInfo *pMutexInfo;
    Aura_ECode ec;
    assert(phMutex);

    pMutexInfo = new MutexInfo();
    if(AURA_NULL == pMutexInfo){
        PERROR("Error:Create Mutex failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
        return AURA_E_OUT_OF_MEMORY;
    }

    if(AURA_NULL == wszName){
        ec = CreateAnonymDir(pMutexInfo->m_szName);
        if(AURA_FAILED(ec)){
            PERROR("Error:Create Mutex failed! \
                file:%s. line:%d. \n",
                __FILE__, __LINE__);
            goto AURA_FAILED;
        }
    }else {
        UnicToAnsi(pMutexInfo->m_szName, wszName);
        if(-1 == mkdir(pMutexInfo->m_szName, 0x777)){
            PTRACE("May create same name mutex.\n");
        }
    }

    semKey = ftok(pMutexInfo->m_szName, 'a');

    pMutexInfo->m_iSemId = semget( semKey, 1, IPC_CREAT | IPC_EXCL | 0666 );
    if ( pMutexInfo->m_iSemId != -1 ){
        if ( semctl( pMutexInfo->m_iSemId, 0, SETVAL, 1 ) == -1  ){
            PERROR("Error:Create Mutex failed! errno:%d.\
            file:%s. line:%d. \n",
            errno, __FILE__, __LINE__);
            semctl( pMutexInfo->m_iSemId, 0, IPC_RMID );
            goto AURA_FAILED;
        }
    }
    else{
        PERROR("Error:Create Mutex failed! errno:%d.\
        file:%s. line:%d. \n",
        errno, __FILE__, __LINE__);
        goto AURA_FAILED;
    }

    *phMutex = (Aura_Handle)pMutexInfo;

    return AURA_NOERROR;

AURA_FAILED:
    delete pMutexInfo;
    return AURA_E_INVALID_OPERATION;
}


AURA_API AuraMutex_Create(
        /* [in] */ Aura_PWChar name,
        /* [in] */ Aura_Bool bLimitToOpenFlag,
        /* [out] */ Aura_Handle * phMutex)
{

    Aura_ECode ec;

    assert(AURA_NULL != phMutex);

    if(bLimitToOpenFlag){
        if(name == AURA_NULL || *name == 0){
            PERROR("Error: Create Mutex failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
            return AURA_E_INVALID_ARGUMENT;
        }

        ec = OpenMutex((Aura_PWChar)name, phMutex);
        if(AURA_FAILED(ec)){
            PERROR("Error: Create Mutex failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
        }
    } else {
        ec = CreateMutex((Aura_PWChar)name, phMutex);
        if(AURA_FAILED(ec)){
            PERROR("Error: Create Mutex failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
        }
    }

    return ec;
}


AURA_API AuraMutex_Lock(
        /* [in] */ Aura_Handle hMutex,
        /* [in] */ Aura_Millisecond timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    MutexInfo *pMutexInfo;
    pMutexInfo = (MutexInfo *)hMutex;
    struct sembuf sSemB;
    int ret;
    ThreadInfo *pThreadInfo;
    Aura_ECode ec;

    if((AURA_NULL == pMutexInfo)
        || (!IsValidHandle(MutexInfo, pMutexInfo))){
        PERROR("Error: Mutex Lock failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    if(AURA_NULL != pResult){
        *pResult = Aura_WaitResult_OK;
    }

    pThreadInfo = GetCurrentThreadInfo();

    sSemB.sem_num = 0;
    sSemB.sem_op  = -1;
    sSemB.sem_flg = SEM_UNDO;

    ec = SetTimeOut(timeOut, pResult);
    if(AURA_FAILED(ec)){
        return ec;
    }


    //Already signaled !!
    if(pThreadInfo->m_uState & INTERRUPT){
        pThreadInfo->m_uState &= ~INTERRUPT;
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_Interrupted;
        }
        goto EXIT;
    }
    if(pThreadInfo->m_uState & TIMEOUT){
        pThreadInfo->m_uState &= ~TIMEOUT;
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_TimedOut;
        }
        goto EXIT;
    }

    pThreadInfo->m_uState = WAITING;
    //BUGBUG:signal may be missed here.

    do{
        ret = semop(pMutexInfo->m_iSemId, &sSemB, 1);
        if (-1  == ret){
            if(EINTR == errno){
                assert(pThreadInfo);
                if(pThreadInfo->m_uState & INTERRUPT){
                    if(AURA_NULL != pResult){
                        *pResult = Aura_WaitResult_Interrupted;
                    }
                    goto EXIT;
                }

                if(pThreadInfo->m_uState & TIMEOUT){
                    if(AURA_NULL != pResult){
                        *pResult = Aura_WaitResult_TimedOut;
                    }
                    goto EXIT;
                }
            } else {
                PERROR("Error: Mutex Lock failed! \
                file:%s. line:%d. \n",
                __FILE__, __LINE__);
                pThreadInfo->m_uState &= ~WAITING;
                return AURA_E_INVALID_OPERATION;
            }
        }
    } while(-1 == ret);


EXIT:
    pThreadInfo->m_uState &= ~INTERRUPT;
    pThreadInfo->m_uState &= ~TIMEOUT;

    return AURA_NOERROR;
}

AURA_API AuraMutex_Unlock(
        /* [in] */ Aura_Handle hMutex)
{
    MutexInfo *pMutexInfo;
    pMutexInfo = (MutexInfo *)hMutex;
    struct sembuf sSemB;
    int ret;

    if((AURA_NULL == pMutexInfo)
        || (!IsValidHandle(MutexInfo, pMutexInfo))){
        PERROR("Error: Mutex Unlock failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    sSemB.sem_num = 0;
    sSemB.sem_op  = 1;
    sSemB.sem_flg = SEM_UNDO;

    ret = semop(pMutexInfo->m_iSemId, &sSemB, 1);
    if (ret  == -1){
        PERROR("Error: Mutex Unlock failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_OPERATION;
    }

    //AuraThread_Yield();

    return AURA_NOERROR;
}

AURA_API AuraMutex_Destroy(
        /* [in] */ Aura_Handle hMutex)
{

    MutexInfo *pMutexInfo;
    pMutexInfo = (MutexInfo *)hMutex;

    if((AURA_NULL == pMutexInfo)
        || (!IsValidHandle(MutexInfo, pMutexInfo))){
        PERROR("Error: Mutex Destroy failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    if(pMutexInfo->m_bCreate){
        semctl( pMutexInfo->m_iSemId, 0, IPC_RMID );
        rmdir(pMutexInfo->m_szName);
    }

    delete pMutexInfo;

    return AURA_NOERROR;
}

// "Event" interface functions;

struct EventInfo : public DLinkNode {
    Aura_UInt32       m_uMagicnumberStart;
    Aura_PWChar     m_wszName;
    Aura_Bool      m_bManual;
    Aura_UInt32       m_uSignal;
    Aura_EventState   m_iState;
    Aura_Handle       m_hEventInfoLock;
    DLinkNode    m_dWaitThreadList;
    Aura_UInt32       m_uMagicnumberEnd;

    EventInfo(Aura_PWChar name, Aura_Bool bManualReset, Aura_EventState initialState)
        {
            m_uMagicnumberStart = MAGIC_START_NUMBER;
            m_uMagicnumberEnd = MAGIC_END_NUMBER;
            if(AURA_NULL != name){
                m_wszName = name;
                Aura_Wcscpy(m_wszName, name);
            } else {
                m_wszName = AURA_NULL;
            }
            m_bManual = bManualReset;
            if(initialState){
                m_iState = initialState;
                m_uSignal = 1;
            } else {
                m_iState = 0;
                m_uSignal = 0;
            }
            m_hEventInfoLock = AURA_NULL;
            m_dWaitThreadList.Initialize();
        }
};

AURA_API AuraEvent_Create(
        /* [in] */ Aura_Bool bManualReset,
        /* [in] */ Aura_EventState initialState,
        /* [out] */ Aura_Handle  * phEvent)
{
    EventInfo *pEventInfo;
    Aura_ECode ec;

    pEventInfo = new EventInfo(AURA_NULL, bManualReset, initialState);
    if(AURA_NULL == pEventInfo){
        PERROR("Error:Create Event failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_OUT_OF_MEMORY;
    }

    ec = CreateLock(&pEventInfo->m_hEventInfoLock);
    if(AURA_FAILED(ec)){
        PERROR("Error:Create Event failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return ec;
    }

    if(AURA_NULL != phEvent){
        *phEvent = (Aura_Handle)pEventInfo;
    }

    return AURA_NOERROR;
}


AURA_API AuraEvent_Wait(
        /* [in] */ Aura_Handle hEvent,
        /* [in] */ Aura_Millisecond timeOut,
        /* [out] */ Aura_WaitResult * pResult,
        /* [out] */ Aura_EventState * pState)
{
    Aura_ECode ec = AURA_NOERROR;
    int ret;
    EventInfo *pEventInfo;
    ThreadInfo *pThreadInfo;
    ThreadRef *pThreadRef;

    sigset_t  SigZeroMask;
    sigset_t  SigNewMask;
    sigset_t  SigOldMask;

    pEventInfo = (EventInfo *)hEvent;

    if((AURA_NULL == pEventInfo)
        || (!IsValidHandle(EventInfo, pEventInfo))){
        PERROR("Error: Evnet Signal failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    sigemptyset(&SigZeroMask);
    sigemptyset(&SigNewMask);
    sigemptyset(&SigOldMask);

    pThreadInfo = GetCurrentThreadInfo();

    if(AURA_NULL != pResult){
        *pResult = Aura_WaitResult_OK;
    }

    ec = MaskSignal(&SigNewMask, &SigOldMask);
    if(AURA_FAILED(ec)){
        PERROR("Error: Wait Signal failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return ec;
    }

    //Has interrupted
    if(INTERRUPT & pThreadInfo->m_uState){
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_Interrupted;
        }
        pThreadInfo->m_uState &= ~INTERRUPT;
        goto EXIT;
    }

    //Set Timer
    ec = SetTimeOut(timeOut, pResult);
    if(AURA_FAILED(ec)){
        goto EXIT;
    }

    //Add thread info to event
    Lock(pEventInfo->m_hEventInfoLock);

    //Has signaled
    if(0 < pEventInfo->m_uSignal){
        if(!pEventInfo->m_bManual)pEventInfo->m_uSignal--;
        Unlock(pEventInfo->m_hEventInfoLock);
        goto EXIT;
    }

    pThreadRef = new ThreadRef(pThreadInfo);
    if(AURA_NULL == pThreadRef){
        PERROR("Error:Wait Event failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        ec = AURA_E_OUT_OF_MEMORY;
        goto EXIT;
    }

    pEventInfo->m_dWaitThreadList.InsertPrev(pThreadRef);
    pThreadInfo->m_uState |= WAITING;

    Unlock(pEventInfo->m_hEventInfoLock);

    //Wait
    while(pThreadInfo->m_uState & WAITING){
        ret = sigsuspend(&SigZeroMask);
        if((ret == -1)&&(EINTR != errno)){
            {
                PERROR("Error:Wait Event failed! \
                file:%s. line:%d. \n",
                __FILE__, __LINE__);
                ec = AURA_E_INVALID_OPERATION;
                goto CLEAN_EXIT;
            }
        }
    }

    if(INTERRUPT & pThreadInfo->m_uState){
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_Interrupted;
        }
        goto CLEAN_EXIT;
    }

    if(TIMEOUT & pThreadInfo->m_uState){
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_TimedOut;
        }
        goto CLEAN_EXIT;
    }

CLEAN_EXIT:
    //enter the eventinfo and clean the threadinfo
    Lock(pEventInfo->m_hEventInfoLock);
    if(!pThreadRef->IsEmpty()){
        pThreadRef->Detach();
    }
    Unlock(pEventInfo->m_hEventInfoLock);
    //BUGBUG:Will down
    if((AURA_NULL != pState)&&(!AURA_FAILED(ec))){
        *pState = (Aura_EventState)pThreadRef->m_uStore;
    }

    delete pThreadRef;

EXIT:
    pThreadInfo->m_uState &= ~INTERRUPT;
    pThreadInfo->m_uState &= ~TIMEOUT;
    UnmaskSignal(&SigOldMask);
    return ec;
}

Aura_ECode SignalManualEvent(EventInfo *pEventInfo)
{
    ThreadInfo *pThreadInfo;
    ThreadRef  *pThreadRef;

    //Has signaled
    if(1 == pEventInfo->m_uSignal){
        assert(pEventInfo->m_dWaitThreadList.IsEmpty());
        return AURA_NOERROR;
    }

    //No Thread waiting for
    if(pEventInfo->m_dWaitThreadList.IsEmpty()){
        pEventInfo->m_uSignal = 1;
        return AURA_NOERROR;
    }

    //Has not signaled
    pEventInfo->m_uSignal = 1;

    //set all threads
    while(&pEventInfo->m_dWaitThreadList != pEventInfo->m_dWaitThreadList.Next()){
        pThreadRef  = (ThreadRef *)pEventInfo->m_dWaitThreadList.Next();
        pThreadInfo = pThreadRef->m_pThreadInfo;
        pThreadRef->Detach();
        pThreadRef->Initialize();
        pThreadRef->m_uStore = (Aura_UInt32)pEventInfo->m_iState;
        kill((pid_t)pThreadInfo->m_iThreadId, SIGUSR1);
    }

    return AURA_NOERROR;
}

Aura_ECode SignalUnmanualEvent(EventInfo *pEventInfo)
{
    ThreadInfo *pThreadInfo;
    ThreadRef  *pThreadRef;

    //Has signaled
    if(0 != pEventInfo->m_uSignal){
        pEventInfo->m_uSignal++;
        return AURA_NOERROR;
    }

    //No Thread waiting for
    if(pEventInfo->m_dWaitThreadList.IsEmpty()){
        pEventInfo->m_uSignal++;
        return AURA_NOERROR;
    }

    //Has not signaled
    pThreadRef  = (ThreadRef *)pEventInfo->m_dWaitThreadList.Next();
    pThreadInfo = pThreadRef->m_pThreadInfo;
    pThreadRef->Detach();
    pThreadRef->Initialize();
    pThreadRef->m_uStore = (Aura_UInt32)pEventInfo->m_iState;
    pEventInfo->m_iState = 0;
    kill((pid_t)pThreadInfo->m_iThreadId, SIGUSR1);

    return AURA_NOERROR;
}

AURA_API AuraEvent_Signal(
        /* [in] */ Aura_Handle hEvent,
        /* [in] */ Aura_EventState state)
{
    Aura_ECode ec = AURA_NOERROR;
    EventInfo *pEventInfo;
    pEventInfo = (EventInfo *)hEvent;

    if((AURA_NULL == pEventInfo)
        || (!IsValidHandle(EventInfo, pEventInfo))){
        PERROR("Error: Evnet Signal failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    Lock(pEventInfo->m_hEventInfoLock);

    pEventInfo->m_iState = state;

    if(pEventInfo->m_bManual){
        ec = SignalManualEvent(pEventInfo);
    } else {
        ec = SignalUnmanualEvent(pEventInfo);
    }

    Unlock(pEventInfo->m_hEventInfoLock);

    return ec;
}

AURA_API AuraEvent_Unsignal(
        /* [in] */ Aura_Handle hEvent)
{
    EventInfo *pEventInfo;
    pEventInfo = (EventInfo *)hEvent;

    if((AURA_NULL == pEventInfo)
        || (!IsValidHandle(EventInfo, pEventInfo))){
        PERROR("Error: Evnet Signal failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    Lock(pEventInfo->m_hEventInfoLock);

    pEventInfo->m_uSignal = 0;
    pEventInfo->m_iState  = 0;

    Unlock(pEventInfo->m_hEventInfoLock);

    return AURA_NOERROR;
}


//BUGBUG: Make sure no one using the event!!!
AURA_API AuraEvent_Destroy(
        /* [in] */ Aura_Handle hEvent)
{
    EventInfo *pEventInfo;
    pEventInfo = (EventInfo *)hEvent;

    if((AURA_NULL == pEventInfo)
        || (!IsValidHandle(EventInfo, pEventInfo))){
        PERROR("Error: Evnet Signal failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    DestroyLock(pEventInfo->m_hEventInfoLock);

    delete pEventInfo;

    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_Create(
        /* [out] */ Aura_Handle * phCriticalSection)
{
    return CreateLock(phCriticalSection);
}

AURA_API AuraCriticalSection_Enter(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    return Lock(hCriticalSection);
}

AURA_API AuraCriticalSection_TryEnter(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    return AURA_E_NOT_IMPLEMENTED;
}

AURA_API AuraCriticalSection_Leave(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    return Unlock(hCriticalSection);
}

AURA_API AuraCriticalSection_Destroy(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    return DestroyLock(hCriticalSection);
}

//#define DEBUG_AURA_SYN
#ifdef DEBUG_AURA_SYN

void ShowStatus( pid_t pid, int status )
{
        int flag = 1;

        printf( "[v7_show_status]: pid = %ul => ", pid );

        if ( WIFEXITED( status ) )
        {
                flag = 0;
                printf( "true if the child terminated normally, that is, "
                                "by calling exit() or _exit(), or "
                                "by returning from main().\n" );
        }

        if ( WEXITSTATUS( status ) )
        {
                flag = 0;
                printf( "evaluates to the least significant eight bits of the "
                        "return code of  the  child  which terminated, which may "
                        "have been set as the"
            "argument to a call to exit() or _exit() or as the argument for a"
            "return  statement  in  the main program.  This macro can only be"
            " evaluated if WIFEXITED returned true. \n" );
        }

        if ( WIFSIGNALED( status ) )
        {
                flag = 0;
                printf( " true if the child process terminated because of a signal"
             " which was not caught.\n" );
        }

        if ( WTERMSIG( status ) )
        {
                flag = 0;
                printf( "  the  number of the signal that caused the child process"
              "to terminate. This macro can only be  evaluated  if  WIFSIGNALED"
              "returned non-zero.\n");
        }

        if ( WIFSTOPPED( status ) )
        {
                flag = 0;
                printf( "  true  if  the  child process which caused the return is"
              "currently stopped; this is only possible if the  call  was  done"
              "using   WUNTRACED  or  when  the  child  is  being  traced  (see"
              "ptrace(2)).\n" );
        }

        if ( WSTOPSIG( status ) )
        {
                flag = 0;
                printf( " the number of the signal which caused the child to stop."
             "This   macro  can  only  be  evaluated  if  WIFSTOPPED  returned"
             "non-zero.\n" );
        }

        if ( flag )
        {
                printf( "Unknown status = 0x%X\n", status );
        }
}

#define Dir L"/tmp/TestMutextt"
void WaitMutexTest(Aura_PVoid pArg)
{
    pid_t pid;
    Aura_WaitResult result;
    Aura_ECode ec;
    Aura_Handle hMutex;

    pid = getpid();

    AuraMutex_Create(Dir, AURA_TRUE, &hMutex);

    while(1){
        printf("****id:%d****time out:%d*****\n", pid, (Aura_Millisecond)pArg);
        ec = AuraMutex_Lock(hMutex, (Aura_Millisecond)pArg, &result);
        if(AURA_FAILED(ec)){
            printf("AURA_FAILED lock thread id:%d.\n", pid);
            return;
        }
        if(Aura_WaitResult_OK != result){
            printf("process:%d time out.\n", pid);
            continue;
        }
        printf("Enter the mutex thread id:%d.\n", pid);
        AuraThread_Sleep(1000, &result);
        printf("Leave the mutex thread id:%d.\n", pid);
        AuraMutex_Unlock(hMutex);
    }

    AuraMutex_Destroy(hMutex);

    return ;
}

void GetProcessInfo(int arg){
    int i;
    pid_t pid;
    pid = wait(&i);
    ShowStatus( pid, i );
}

int mutexTest(int argc, char *argv[])
{
    pid_t pid[5];
    Aura_WaitResult result;
    int i = 0;
    char szTime[AURA_MAX_DIR];
    int time;
    Aura_Handle hMutex;

    //SUB PROCESS
    if(argv[0][0] == '1'){
        WaitMutexTest((Aura_PVoid)atoi(argv[1]));
        exit(0);
    }

    if(SIG_ERR == signal(SIGCHLD, GetProcessInfo)){
        PERROR("Error: Set Central Context failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_OPERATION;
    }

    AuraMutex_Create(Dir, AURA_FALSE, &hMutex);
    AuraMutex_Lock(hMutex, AURA_INFINITE, &result);
    printf("main enter semaphore\n");
    getchar();
    for(i = 0; i < 5; i++){
        pid[i] = fork();
        if ( pid[i] < 0 )
                printf( "fail to fork child\n" );
        else if ( pid[i] == 0 ) // child process
        {
            time = i*5000 +1000;
            sprintf(szTime, "%d", time);
            int execRet = execlp("./aura.exe", "1", szTime, AURA_NULL);
            if(execRet < 0){
                PERROR("Error: execlp failed! Error number:0x%d. \
                file:%s. line:%d. \n",
                errno, __FILE__, __LINE__);
                exit(errno);
            }
            return 0;
        }
    }
    getchar();
    printf("main exit semaphore\n");

    AuraMutex_Unlock(hMutex);

    getchar();

    for(i = 0; i < 5; i++){
        kill(pid[i], SIGKILL);
    }
    AuraMutex_Destroy(hMutex);
    return 0;
}

struct eventArg
{
    Aura_Handle hEvent;
    Aura_Millisecond timeout;
};


void threadfuncEventWait(Aura_PVoid pArg)
{
    eventArg *eArg;
    Aura_Handle hEvent;
    Aura_Millisecond timeout;
    eArg = (eventArg *)pArg;
    pid_t pid = getpid();
    Aura_WaitResult Result;


    hEvent = eArg->hEvent;
    timeout = eArg->timeout;
    printf("Enter thread.\n");

    while(1){

        printf("Enter Event Wait thread id:%d timeout:%d.\n", pid, timeout);
        AuraEvent_Wait(hEvent, timeout, &Result, AURA_NULL);
        if(Aura_WaitResult_TimedOut == Result){
            printf("Time out thread id:%d, timeout:%d.\n",  pid, timeout);
        } else if(Aura_WaitResult_Interrupted == Result){
            printf("Interrupt thread id:%d\n", pid);
        } else {
            printf("Get signal thread id:%d\n", pid);
        }
        AuraThread_Sleep(100, AURA_NULL);
    }

}

#define THREAD_NUMBER 5
#define MANUL        AURA_TRUE

int main(int argc, char *argv[])
{
    Aura_Handle hThread[THREAD_NUMBER];
    int i = 0;
    Aura_Handle hEvent;
    Aura_ECode ec;
    eventArg eArg;

    ec = AuraEvent_Create(AURA_NULL, MANUL, 0, &hEvent);
    if(AURA_FAILED(ec)){
        printf("Create event failed\n");
        return -1;
    }

    eArg.hEvent = hEvent;

    for(i = 0; i < THREAD_NUMBER; i++){

        if(0 == i){
            eArg.timeout = AURA_INFINITE;
        } else {
            eArg.timeout = i*5000;
        }

        ec = AuraThread_Create(
            threadfuncEventWait,
            (Aura_PVoid)&eArg,
            0,
            &hThread[i],
            AURA_NULL);
        if(AURA_FAILED(ec)){
            printf("^^^^^^^^^^^^^^^^^^^^^%d\n", i);
        }else{
            printf("Create thread succeed\n");
        }
    }

    printf("Press any key send signal, q to quit\n");
    while('q' != getchar()){
        if(MANUL){
            if(i%2){
                AuraEvent_Signal(hEvent, 0);
            }else{
                AuraEvent_Unsignal(hEvent);
            }
            i++;
        }else {
            AuraEvent_Signal(hEvent, 0);
        }
    }

    if(MANUL){
        AuraEvent_Unsignal(hEvent);
    }

    printf("Press any key to interrupt, q to quit\n");
   while('q' != getchar()){
        if(i >= THREAD_NUMBER){
            i = 0;
        }
        AuraThread_Interrupt(hThread[i]);
        i++;
    }

    for(i = 0; i < THREAD_NUMBER; i++){
        AuraThread_Abort(hThread[i], 1);
        AuraThread_Sleep(100, AURA_NULL);
        AuraThread_Destroy(hThread[i]);
    }

    AuraThread_Sleep(100, AURA_NULL);
    AuraEvent_Destroy(hEvent);
}

#endif //DEBUG_AURA_SYN

